/**
filename: w25qxx_spi.c
author: Mr.GAO
website: www.genbotter.com
version: V1.0

instructions of w25qxx
**/
#include "spi_w25qxx.h"
#include "i2c_oled.h"
#include "stdio.h"
/*
 ******************************************************************************
 ** 1. 16M = 256Block  1Block = 16Sector 1Sector = 16页 1页=256bytes
 ** 2. 最小擦除单位 : 1Sector = 4K     
 ******************************************************************************
 */

void w25qxx_init(void){
	// 使能外设时钟
	w25qxx_rcu_init();
	
	// IO口进行配置，使之复用为SPI0， PA4\PA5\PA6\PA7，NSS\SCK\MISO\MOSI
	w25qxx_io_init();
	
	// SPI0初始化
	w25qxx_spi_init();
	
	spi_enable(SPI0);
}

// 使能外设时钟
void w25qxx_rcu_init(void){
	rcu_periph_clock_enable(RCU_GPIOA);     //使能GPIOA时钟
	rcu_periph_clock_enable(RCU_AF);    //使能AF时钟
	rcu_periph_clock_enable(RCU_SPI0);  //使能SPI0时钟
}
	
// IO口进行配置，使之复用为SPI0， PA4\PA5\PA6\PA7，NSS\SCK\MISO\MOSI
void w25qxx_io_init(void){
	gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // MISO
	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7); // SCK\MOSI
	gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);// NSS片选口
}
	
// SPI0初始化
void w25qxx_spi_init(void){
	spi_parameter_struct spi_struct;
	spi_struct.device_mode = SPI_MASTER;    /*!< SPI master*/
    spi_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;  /*!< SPI transfer type */
    spi_struct.frame_size =  SPI_FRAMESIZE_8BIT;       /*!< SPI frame size */
    spi_struct.nss = SPI_NSS_SOFT;                     /*!< SPI NSS control by software */
    spi_struct.endian = SPI_ENDIAN_MSB;                /*!< SPI big endian or little endian */
    spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /*!< SPI clock phase and polarity */
    spi_struct.prescale = SPI_PSC_8;                          /*!< SPI prescaler factor */
	
	spi_init(SPI0, &spi_struct);
}

// 如果SR-1的BUSY位为1的话，一直等待，直到BUSY位为0，结束等待
void w25qxx_wait_busy(void){
	while((w25qxx_read_sr(W25QXX_SR_ID_1) & 0x01) == 0x01){

	}
}

// 读状态寄存器
/*
**********************************************************
** desc : 读状态寄存器
** note : BIT 7   | 6  | 5  | 4   | 3  | 2   | 1  |  0
**            SPR | RV | TB | BP2 | BP1| BP0 | WEL|  BUSY
**        SPR : 默认0,状态寄存器保护位,配合WP使用
**        TB,BP2,BP1,BP0 : FLASH区域写保护设置
**        WEL : 写使能锁定
**        BUSY:忙标记位(1,忙;0,空闲)
**********************************************************
*/
uint8_t w25qxx_read_sr(uint8_t sregister_id){
	uint8_t command, result;
	switch(sregister_id){
		case W25QXX_SR_ID_1:
			command = W25QXX_READ_STATUS_REGISTER_1;
		break;
		case W25QXX_SR_ID_2:
			command = W25QXX_READ_STATUS_REGISTER_2;
		break;
		case W25QXX_SR_ID_3:
			command = W25QXX_READ_STATUS_REGISTER_3;
		break;
		default:
			command = W25QXX_READ_STATUS_REGISTER_1;
		break;
	}
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(command);
	result = w25qxx_swap(0xFF);
	w25qxx_cs_disable(W25QXX_ID_1);
	
	return result;
}

// 读flash的数据
// *p_buffer 读回的数据的存放位置
void w25qxx_read(uint8_t *p_buffer, uint32_t read_addr, uint16_t num_read_bytes){
	uint16_t i;
	
	w25qxx_cs_enable(W25QXX_ID_1);
	
	w25qxx_swap(W25QXX_READ_DATA); //发送读数据的指令
	w25qxx_swap((uint8_t)(read_addr >> 16));  //发送24bit地址
	w25qxx_swap((uint8_t)(read_addr >> 8));
	w25qxx_swap((uint8_t)(read_addr));
	
	for(i=0; i < num_read_bytes; i++){
		p_buffer[i] = w25qxx_swap(0xFF);
	}
	
	w25qxx_cs_disable(W25QXX_ID_1);
}


/*
**************************************************************
** desc : 写W25Qxx状态寄存器
** note : 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
**************************************************************
*/
static uint8_t w25qxx_buffer[4096];  //用来存放从sector读出的bytes
void w25qxx_write(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint32_t sec_num;
	uint16_t sec_remain;
	uint16_t sec_off;
	uint16_t i;
	
	sec_num	= write_addr / 4096;              //要写入的位置处在第sec_num个扇区上
	sec_off = write_addr % 4096;
	
	sec_remain = 4096 - sec_off;
	
	if(num_write_bytes <= sec_remain){
		w25qxx_read(w25qxx_buffer, sec_num * 4096, 4096);  //扇区的数据读出来
		
		for(i = 0; i < sec_remain; i++){
			if(w25qxx_buffer[i + sec_off] != 0xFF)  //说明这个扇区的第i+sec_off位没有擦除
				break;
		}
		
		if(i < sec_remain){ // 扇区没有擦除
			w25qxx_erase_sector(sec_num * 4096);
			for(i = 0; i < sec_remain; i++){
				w25qxx_buffer[i + sec_off] = p_buffer[i];
			}
			w25qxx_write_nocheck(w25qxx_buffer, sec_num * 4096, 4096);
		}else{              // 扇区sec_remain部分是擦除过的
			w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
		}
	}else{
		w25qxx_read(w25qxx_buffer, sec_num * 4096, 4096);  //扇区的数据读出来
		
		for(i = 0; i < sec_remain; i++){
			if(w25qxx_buffer[i + sec_off] != 0xFF)  //说明这个扇区的第i+sec_off位没有擦除
				break;
		}
		
		if(i < sec_remain){ // 扇区没有擦除
			w25qxx_erase_sector(sec_num * 4096);
			for(i = 0; i < sec_remain; i++){
				w25qxx_buffer[i + sec_off] = p_buffer[i];
			}
			w25qxx_write_nocheck(w25qxx_buffer, sec_num * 4096, 4096);
		}else{              // 扇区sec_remain部分是擦除过的
			w25qxx_write_nocheck(p_buffer, write_addr, sec_remain);
		}
		
		write_addr += sec_remain;
		p_buffer += sec_remain;
		num_write_bytes -= sec_remain;
		w25qxx_write(p_buffer, write_addr, num_write_bytes);
	}
		
	//判断读出来的数据是否都为0xFF
	//扇区是否删除
	//判断是否跨页
}

// 调用之前先确保扇区删除
void w25qxx_write_nocheck(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint16_t page_remain = 256 - write_addr % 256;
	
	if(num_write_bytes <= page_remain){
		w25qxx_write_page(p_buffer, write_addr, num_write_bytes);
	}else{
		w25qxx_write_page(p_buffer, write_addr, page_remain);
		p_buffer += page_remain;
		write_addr += page_remain;
		num_write_bytes -= page_remain;
		w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
	}
}

// page program
// 保证没有跨页写的前提下调用此函数往某个页上写内容
void w25qxx_write_page(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint16_t i;
	
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_PAGE_PROGRAM);
	w25qxx_swap((uint8_t)(write_addr >> 16));  //发送24bit地址
	w25qxx_swap((uint8_t)(write_addr >> 8));
	w25qxx_swap((uint8_t)(write_addr));
	
	for(i = 0; i < num_write_bytes; i++){
		w25qxx_swap(p_buffer[i]);
	}
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_erase_sector(uint32_t sector_addr){
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_SECTOR_ERASE_4KB);
	w25qxx_swap((uint8_t)(sector_addr >> 16));
	w25qxx_swap((uint8_t)(sector_addr >> 8));
 
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_erase_chip(void){
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_CHIP_ERASE);
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_write_enable(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_WRITE_ENABLE);
	w25qxx_cs_disable(W25QXX_ID_1);
}

void w25qxx_write_disable(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_WRITE_DISABLE);
	w25qxx_cs_disable(W25QXX_ID_1);
}

// 低电量休眠
void w25qxx_power_down(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_POWER_DOWN);
	w25qxx_cs_disable(W25QXX_ID_1);
}

// 唤醒
void w25qxx_wake_up(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_RELEASE_POWER_DOWN_HPM_DEVICE_ID);
	w25qxx_cs_disable(W25QXX_ID_1);
}

/*
brief：使能片选引脚cs
cs_id: cs引脚的序号，即第几个w25qxx flash
*/
void w25qxx_cs_enable(uint8_t cs_id){
	switch(cs_id){
		case W25QXX_ID_1:
			gpio_bit_reset(GPIOA, GPIO_PIN_4);
		break;
		default:
			break;
	}
}

void w25qxx_cs_disable(uint8_t cs_id){
	switch(cs_id){
		case W25QXX_ID_1:
			gpio_bit_set(GPIOA, GPIO_PIN_4);
		break;
		default:
			break;
	}
}

/**
 * @brief W25Q564芯片读取ID
 * 
 * @param MID 制造商ID（Manufacture ID） 
 * @param DID 设备ID （Device ID)
 */
void w25qxx_read_id(uint8_t *MID, uint16_t *DID)
{
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_JEDEC_ID);
	*MID = w25qxx_swap(W25Qxx_DUMMY_BYTE);
	*DID = w25qxx_swap(W25Qxx_DUMMY_BYTE);
	*DID <<= 8;
	*DID |= w25qxx_swap(W25Qxx_DUMMY_BYTE);
	w25qxx_cs_disable(W25QXX_ID_1);	
}

/*

*/
uint8_t w25qxx_swap(uint8_t byte_to_send){
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) == RESET){ // 等待SPI发送缓冲器为空
	}
	spi_i2s_data_transmit(SPI0, (uint16_t)byte_to_send);
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS) == SET){ // 等待通信结束
	}
	
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) == RESET){// 等待SPI接收缓冲器非空
	}	
	return spi_i2s_data_receive(SPI0);
}

void w25qxx_printdata(uint32_t beg_addr, uint32_t len)
{
	uint32_t i;
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap((uint8_t)(beg_addr >> 16)); 	/* 交换发送地址23-16位 */
	w25qxx_swap((uint8_t)(beg_addr >> 8));		/* 交换发送地址15-8位 */
	w25qxx_swap((uint8_t)(beg_addr));			/* 交换发送地址7-0位 */

	for (i = 0; i < len; i++)
	{
		uint32_t current_address = beg_addr + i;
		uint32_t sector_index = current_address / W25Qxx_SECTOR_SIZE;
		uint32_t page_index = current_address / W25Qxx_PAGE_SIZE;
		if (current_address % W25Qxx_SECTOR_SIZE == 0)
		{
			printf("=============================PAGE %d SECTOR %d ==================================", page_index, sector_index % W25Qxx_SECTOR_COUNT_PER_PAGE);
		}
		if (i % 16 == 0)
		{
			printf("\r\n");
			printf("%#6x | ", beg_addr + 16 * i);
		}
		printf("%#x ", w25qxx_swap(W25Qxx_DUMMY_BYTE));
	}
	printf("\r\n");
	w25qxx_cs_disable(W25QXX_ID_1);
}


void w25qxx_test(void)
{
	uint8_t MID = 0;
	uint16_t DID = 0;
	printf("w25 test start\r\n");
	bsp_i2c_init();
	oled_init();
	oled_clear_all();
	oled_set_char_size(kcharsize_8x16);
	oled_show_string(1, 1, "MID:     DID:");
	oled_show_string(1, 2, "W:");
	oled_show_string(1, 3, "R:");
	
	w25qxx_init();
	w25qxx_read_id(&MID, &DID);
	oled_show_hex_num(1, 5, MID, 2);
	oled_show_hex_num(1, 12, DID, 4);

	uint8_t array_to_write[] = {0x11, 0x22, 0x33, 0x44};
	w25qxx_erase_sector(0x000000);
	w25qxx_printdata(0x0000FE, 4);
	w25qxx_write(array_to_write, 0x0000FE, 4);
	w25qxx_printdata(0x0000FE, 4);

}